<template>
  <div class="cheat_out" @click="clearBtnId">
    <div class="mid">
      <div :class="['mid_talk_list', state.linkList.length === 0 && 'mid_talk_list-h1']">
        <el-scrollbar v-if="state.chatLogs.length">
          <!-- 对对话记录 -->
          <div v-for="(item, index) in state.chatLogs" :key="index" class="one_talk">
            <div v-if="item.question" :key="item.id" class="user_talk">
              <div class="user_talk_kuang">
                <pre class="user_talk_font">{{ item.question }}</pre>
                <div v-if="(item.files || []).length > 0" class="">
                  <div v-for="(ite1, key) in item.files" :key="key">
                    <div v-if="ite1.url" class="file-s-l">
                      <div class="fileBox" v-if="isIMG(ite1.url)">
                        <el-image class="elimage" :src="ite1.url" />
                      </div>
                      <div class="fileBox" v-else>
                        <img class="img" :src="getUrlSuffix(ite1.url)" alt="" srcset="" />
                        <span>附件{{ 1 + key }}</span>
                      </div>
                    </div>
                  </div>
                </div>
              </div>
              <div class="user_avatar">{{ user.nickname.slice(0, 1) }}</div>
            </div>
            <!-- AI回答的 -->
            <div
              class="gpt_talk"
              :key="item.id"
              @mouseenter="
                e => {
                  setBntId(e, item.id);
                }
              "
              @mouseleave="clearBtnId"
              @click="
                e => {
                  setBntId(e, item.id);
                }
              "
            >
              <div class="gpt_avatar">
                <el-image :src="avatar ? uploadFileGet(avatar) : chatImg" />
              </div>
              <!-- 如果 screenStatus  =true 且 index === state.chatLogs.length - 1  将div渲染到#app下面 -->
              <teleport v-if="screenStatus" to="#app">
                <div class="gpt_talk-full">
                  <div class="gpt_talk_font">
                    <!-- 单词消消乐 -->
                    <template v-if="item.answer && !['4'].includes(props.exts.botFlag)">
                      <span v-if="props.exts.botFlag === '1'">
                        <MessageItem :type="props.exts.botFlag" :answer="item.answer" />
                      </span>
                      <Render v-else :text="item.answer" />
                    </template>
                  </div>
                  <!-- 消消乐生成成功后，在GPTTALK外层渲染资源按钮 -->
                  <div class="icon_out mgright" @click="fullScreenFun(false)">
                    <div class="reset-sc">
                      <el-image :src="fullScreenIcon" class="edit-icon"></el-image>
                      缩小
                    </div>
                  </div>
                </div>
              </teleport>
              <div
                v-else
                :class="`gpt_talk_kuang  ${screenStatus && index === state.chatLogs.length - 1 ? 'gpt_talk-full' : ''}`"
              >
                <div class="gpt_talk_font">
                  <!-- 展示chat内容 -->
                  <template v-if="item.answer">
                    <Render :text="item.answer" />
                  </template>
                  <Dot v-if="state.loading && index === state.chatLogs.length - 1" />
                </div>
                <!-- 消消乐生成成功后，在GPTTALK外层渲染资源按钮 -->
                <div class="gn_btn_out gn_btn_out_pc">
                  <div
                    :class="index === state.chatLogs.length - 1 ? 'btn_group' : 'btn_group left_move'"
                    v-if="props.exts.botFlag === '1' && !state.loading && index === state.chatLogs.length - 1"
                  >
                    <div class="icon_out mgright" @click="refreshlog(item, index)">
                      <div class="reset-sc">
                        <el-image :src="refreshIcon" class="edit-icon"></el-image>
                        重新生成
                      </div>
                    </div>

                    <div class="icon_out mgright" @click="fullScreenFun(true)">
                      <div class="reset-sc">
                        <el-image :src="fullScreenIcon" class="edit-icon"></el-image>
                        放大
                      </div>
                    </div>
                  </div>
                </div>
              </div>
            </div>
          </div>
          <div id="scrollDom2" class="scrollDomclass2"></div>
          <div id="scrollDom" class="scrollDomclass"></div>
        </el-scrollbar>
        <div class="welcome_out" v-else>
          <div class="welcome_font">欢迎您的到来！ 很高兴在数字教材对话与您见面</div>
          <div class="welcome_font_small">现在直接在下方输入内容对话吧</div>
        </div>
      </div>
      <div class="mid_bottom">
        <div v-if="state.loading" class="stop_btn">
          <el-image class="stop_icon" :src="state.stopbtnicon"></el-image>
          生成中
        </div>
        <slot name="input"></slot>
      </div>
    </div>
  </div>
</template>

<script setup lang="ts">
import { chatLogGet, stopChat } from '@/services/chat';
import { api, model, modelId, modelType } from '@/utils/request';
import { getStorage, saveStorage } from '@/utils';
import Dot from '@/components/dot.vue';
import { fetchEventSource } from '@microsoft/fetch-event-source';
import { reactive, onMounted, watch, nextTick, ref, computed } from 'vue';
import { ElMessage } from 'element-plus';
import _ from 'lodash';
import refreshIcon from '@/assets/chat/refreshGray.svg';
import fullScreenIcon from '@/assets/chat/fullScreen.svg';

import stopicon from '@/assets/chat/stop.svg';
// 教案设计
import chatImg from '@/assets/ai/aiAvatar.png';
import MessageItem from './messageItem.vue';

import fileType from '@/assets/fileTypes/port';
import Render from './render.vue';
import { uploadFileGet } from '@/services/file';
import { useAppStore } from '@/store/modules/app';

const appStore = useAppStore();
// 用户信息
const user = computed(() => appStore.user) as any;
const props = defineProps({
  question: String,
  selectId: String,
  list: Array,
  exts: Object as any, // 配置
  prompts: {
    type: Array,
    default: () => [],
  },
  // 管理上下文id
  conversationId: {
    type: String,
    default: '',
  },
  isEnd: {
    type: Boolean,
    default: true,
  },
  loading: {
    type: Boolean,
    default: false,
  },
  xxlGame: {
    type: Function,
    default: () => {},
  },
  catalogTitle: String,
  // 头像
  avatar: {
    type: String,
    default: '',
  },
});

const botId = getStorage('botId');

const state = reactive({
  question: '',
  questionMax: 2400,
  loading: false,
  answer: '',
  // 右侧对话记录
  chats: [] as any,
  chatsToday: [] as any,
  chatsYesterday: [] as any,
  chatsEarly: [] as any,
  // 左侧对话记录
  chatLogs: [] as any,
  chatId: '',
  modelTypes: [] as any,
  modelType,
  model,
  modelId,
  modelTypeTemp: [] as any,
  drawerOpen: false,
  refreshIndex: 0,
  refreshId: '',
  refreshItem: {} as any,
  showBtnId: '',
  stopbtnicon: stopicon,
  // 对话记录列表
  // 新ai, diffy相关
  conversation_id: '',
  task_id: '',
  // 上传文件
  loadingInstance: null as any,
  audioPath: '',
  // 输入文件url
  linkUrl: '',
  linkUrlTemp: '',
  linkList: [
    // {
    //   type: 'document',
    //   transfer_method: 'remote_url',
    //   url: 'https://huashujiaoyu.com/static/files/202412261730302345.docx',
    // },
  ] as any,
  // 文件类型
  wolds: 'txt,doc,docx,pdf,ppt,pptx,xlsx,xls',
  imgs: 'jpg,jpeg,png,gif,webp,svg',
  audios: 'mp3,m4a,wav,webm,amr',
  videos: 'mp4,mov,mpeg,mpga',
  // 深度学习
  fileList: [] as any,
  // 保存到我的文档
  saveSHow: false,
  // 音频加载中
  audioLoad: [
    { name: '角色声音模拟', id: '1' },
    { name: '情感、语速模拟', id: '2' },
    { name: '听力音频生成', id: '3' },
  ],
  // 音频加载状态
  loadAudioActive: '0',
  // 是否展示思考
  showtThink: false,
  audioUrlLink: '',
  hearingRoleList: [] as any,
  hearingRoleObj: {} as any,
  // 判断是否是第二次操作
  isTheSecond: false,
  rightExt: {} as any,
  isEndActive: true,
  // 深度学习
  deepLearningList: [] as any,
  saveStr: '', // 回答内容
  // 英语听力状态
  listenCode: 0,
  // 深度思考
  expandDepthActive: false,
});

const setBntId = (e: any, id?: any) => {
  e.stopPropagation();
  state.showBtnId = id;
};
const clearBtnId = () => {
  state.showBtnId = '';
};

const getChatLog = async () => {
  if (state.chatId) {
    const { errCode, data }: any = await chatLogGet({
      chatId: state.chatId,
    });

    if (errCode === 0) {
      state.chatLogs = _.map(data, v => ({
        ...v,
        files: JSON.parse(v.filePath || '[]'),
      }));
      nextTick(() => {
        scrollToBottom();
      });

      state.conversation_id = state.chatLogs[0].conversation_id;
      state.isEndActive = false;
    }
  } else {
    state.chatLogs = [];
  }
};

const emits = defineEmits(['aiAnswerOrer', 'roleConfirm']);

const onEnded = async () => {
  state.loading = false;
  if (!state.chatLogs[state.chatLogs.length - 1].answer) {
    // 没有返回内容
    state.chatLogs[state.chatLogs.length - 1].answer = '检测到内容包含敏感词，说点别的吧~';
  }
  // // 考虑存入files
  // const { errCode, data } = await chatLogAdd({
  //   botId,
  //   chatId: state.chatId,
  //   modelType: state.modelType,
  //   question: state.chatLogs[state.chatLogs.length - 1].question,
  //   answer: state.chatLogs[state.chatLogs.length - 1].answer,
  //   conversation_id: state.conversation_id,
  //   filePath: JSON.stringify(state.chatLogs[state.chatLogs.length - 1].files),
  // });
  // if (errCode === 0) {
  //   state.chatLogs[state.chatLogs.length - 1].id = data.id;
  //   state.chatId = data.chatId;
  //   // 回答结束了
  //   setTimeout(() => emits('aiAnswerOrer', data.chatId), 500);
  // }
};

// 重新生成
const onEndedrefresh = async () => {
  state.loading = false;
  if (!state.chatLogs[state.refreshIndex].answer) {
    // 没有返回内容
    state.chatLogs[state.refreshIndex].answer = '检测到内容包含敏感词，说点别的吧~';
  }
  // await chatLogAdd({
  //   botId,
  //   id: state.refreshId,
  //   chatId: state.chatId,
  //   modelType: state.modelType,
  //   question: state.chatLogs[state.refreshIndex].question,
  //   answer: state.chatLogs[state.refreshIndex].answer,
  //   conversation_id: state.conversation_id,
  // });
};

const chatQuestion = (question: string) => {
  state.question = question;
  state.isTheSecond = true;
  state.showtThink = false;
  chatStart();
};

const chatStart = async () => {
  if (state.loading) {
    ElMessage.error('请等待生成结果');
    return;
  }
  if (!state.question) {
    ElMessage.error('请输入内容');
    return;
  }

  state.loading = true;
  // 新的请求方法
  // import.meta.env.VITE_TYPE，获取配置的api,上线的时候要取这个
  // conversation_id,是说明请求的是同一条记录
  let args = {
    botId,
    query: state.question,
    // response_mode: 'streaming',
    // conversation_id: state.conversation_id,
    user: user.id,
  } as any;

  if (state.conversation_id) {
    args = { ...args, conversation_id: state.conversation_id };
  }

  if (!_.isEmpty(state.linkList)) {
    args = { ...args, files: state.linkList };
  }
  state.chatLogs.push({
    question: state.question,
    files: state.linkList,
    answer: '',
  });
  state.question = '';
  state.linkList = [];

  // if (props.exts.botFlag === '1') {
  //   // 单词消消乐
  //   args.callback = () => {
  //     getChatLog();
  //   };

  //   props.xxlGame({
  //     ...args,
  //     id: props.selectId,
  //     chatId: state.chatId,
  //     modelType: state.modelType,
  //   });
  //   scrollToBottom();
  // }
  chatSendContent(args);
};

const chatSendContent = async (args: any) => {
  scrollToBottom();
  // 动态设置请求的接口
  fetchEventSource(`${api}/chat/chat-messages`, {
    headers: {
      // Authorization: `Bearer ${token}`,
      'Content-Type': 'application/json',
    },
    method: 'post',
    body: JSON.stringify({ ...args }),
    openWhenHidden: true,
    onmessage: e => {
      let { type, content, conversation_id, task_id } = JSON.parse(e.data) || {};
      if (type === 'message') {
        state.conversation_id = conversation_id;
        state.task_id = task_id;
        // 格式1处理
        if (content.indexOf('> 💭') !== -1) {
          content = content.replace('>', '');
          state.chatLogs[state.chatLogs.length - 1].answer += '<div class="dify-res1">';
        }
        if (content === '\n\n\n\n') {
          state.chatLogs[state.chatLogs.length - 1].answer += '</div>';
        }
        if (content.indexOf('\n>') !== -1) {
          content = content.replace('\n>', '\n');
        }
        // 格式2处理
        if (content.indexOf('<think>') !== -1) {
          state.chatLogs[state.chatLogs.length - 1].answer += '<div class="">Thinking</div>';
          state.chatLogs[state.chatLogs.length - 1].answer += '<div class="dify-res1">';
        }
        if (content.indexOf('</think>') !== -1) {
          state.chatLogs[state.chatLogs.length - 1].answer += '</div>';
        }
        state.chatLogs[state.chatLogs.length - 1].answer += content;
        scrollToBottom();
      }
      if (type === 'end') {
        // 结束了, 这个可以添加log记录
        state.linkUrlTemp = '';
        state.audioPath = '';
        onEnded();
      }
    },
    onerror: err => {
      // eventSource.close();
      throw err;
    },
    onclose: () => {
      state.question = '';
      state.loading = false;
    },
  });
};

const refreshlog = async (item: any, indexitem: any) => {
  clearBtnId();
  state.refreshIndex = indexitem;
  state.refreshId = item.id;
  state.loading = true;
  item.answer = '';
  // 新的请求方法
  // import.meta.env.VITE_TYPE，获取配置的api,上线的时候要取这个
  // conversation_id,是说明请求的是同一条记录
  let args = {
    botId,
    // inputs: { modelType: state.model },
    query: item.question,
    user: user.id,
  } as any;
  if (state.conversation_id) {
    args = { ...args, conversation_id: state.conversation_id };
  }
  const files = JSON.parse(item.filePath || '[]');
  if (files.length > 0) {
    args = { ...args, files };
  }
  fetchEventSource(`${api}/chat/chat-stream`, {
    headers: {
      // Authorization: `Bearer ${token}`,
      'Content-Type': 'application/json',
    },
    method: 'post',
    body: JSON.stringify({ ...args }),
    openWhenHidden: true,
    onmessage: e => {
      let { type, content, conversation_id, task_id } = JSON.parse(e.data) || {};
      if (type === 'message') {
        state.conversation_id = conversation_id;
        state.task_id = task_id;
        // // 格式1处理
        // if (content.indexOf('> 💭') !== -1) {
        //   content = content.replace('>', '');
        //   state.chatLogs[state.refreshIndex].answer += '<div class="dify-res1">';
        // }
        // if (content === '\n\n\n\n') {
        //   state.chatLogs[state.refreshIndex].answer += '</div>';
        // }
        // if (content.indexOf('\n>') !== -1) {
        //   content = content.replace('\n>', '\n');
        // }
        // 格式2处理
        state.chatLogs[state.refreshIndex].answer += content;
        scrollToBottom();
      }
      if (type === 'end') {
        // 结束了, 这个可以添加log记录
        onEndedrefresh();
      }
    },
    onerror: err => {
      // eventSource.close();
      // 抛出错误，就会停止发送信息
      // throw err;
    },
    onclose: () => {
      state.question = '';
      state.loading = false;
    },
  });
};

// 放大操作
const screenStatus = ref(false);
const fullScreenFun = (status: boolean) => {
  screenStatus.value = status;
};

// 暂停
const chatEnd = async () => {
  // if (1) return;
  await stopChat({ taskId: state.task_id, user: user.id, botId });
  state.loading = false;
  setTimeout(() => {
    state.chatLogs[state.chatLogs.length - 1].answer += '\n[已停止生成]';
  }, 500);
};

watch(
  () => props.isEnd,
  val => {
    state.isEndActive = val;
  },
  { immediate: true, deep: true },
);

watch(
  () => props.conversationId,
  newVal => {
    if (newVal) {
      state.conversation_id = newVal;
    }
  },
  {
    immediate: true,
  },
);

// tab切换 或者第一次赋值
watch(
  () => state.modelType,
  async () => {},
  { immediate: true },
);

watch(
  () => props.question,
  (newVal: any) => {
    state.question = newVal;
  },
);

const getRightLogs = (id: string) => {
  state.chatId = id;
  getChatLog();
};

watch(
  () => props.selectId,
  (newVal: any) => {
    getRightLogs(newVal);
  },
  {
    immediate: true,
    deep: true,
  },
);
const scrollToBottom = () => {
  const scrollDom = document.getElementById('scrollDom');
  const scrollDom2 = document.getElementById('scrollDom2');
  if (scrollDom) {
    scrollDom.scrollIntoView();
    setTimeout(() => {
      scrollDom2?.scrollIntoView();
    }, 0);
  }
};
watch(
  () => props.list,
  (newVal: any) => {
    state.chatLogs = newVal;

    if (newVal.length > 0) {
      scrollToBottom();
    }
  },
  {
    immediate: true,
    deep: true,
  },
);

watch(
  () => props.loading,
  val => {
    state.loading = val;
    appStore.updateState({ chatActive: val });
  },
  { immediate: true, deep: true },
);

watch(
  () => state.loading,
  val => {
    appStore.updateState({ chatActive: val });
  },
);

const newSpeak = async (next: Function) => {
  if (state.loading) {
    ElMessage.error('请等待生成结果');
    return;
  }
  state.chatId = '';
  state.question = '';
  state.chatLogs = [];
  state.conversation_id = '';
  next(0);
};

onMounted(async () => {
  const query = getStorage('query');
  if (!query) return;
  saveStorage('query', '');
  state.chatLogs.push({
    question: query,
    files: state.linkList,
    answer: '',
  });
  if (state.loading) {
    return;
  }
  state.loading = true;
  const args = {
    // inputs: { modelType: state.model },
    query: query,
    user: user.id,
    botId,
  };
  await chatSendContent(args);
});

const changeBorderColor = (type: any) => {
  const textarea = document.querySelector('.shuru_kuang');
  if (textarea) {
    if (type === 0) {
      textarea.setAttribute('style', 'border: 1px solid #dee8f9 !important;');
    } else {
      setTimeout(() => {
        scrollToBottom();
      }, 100);
      textarea.setAttribute('style', 'border: 1px solid #3C7EFF !important;');
    }
  }
};

const keydownHandle = (e: any, question: string) => {
  state.question = question;
  chatStart();
};

const getUrlSuffix = (filename: any) => {
  const suffix: any = filename.substring(filename.lastIndexOf('.') + 1); // "txt"
  return fileType[suffix.toLocaleLowerCase()];
};

const isIMG = (filename: any) => {
  const suffix: any = filename.substring(filename.lastIndexOf('.') + 1); // "txt"
  const imgs = 'jpg,jpeg,png,gif,webp,svg';
  return imgs.includes(suffix);
};

defineExpose({
  changeBorderColor,
  keydownHandle,
  newSpeak,
  chatStart,
  chatQuestion,
});
</script>

<style lang="scss">
@use './index_noscoped.scss';
</style>
<style lang="scss" scoped>
@use './index.scss';
</style>
